//-----------------------------------------------------------------------
// <copyright file="GacHelper.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>Jason Birth [4 Feb 08: Mike Fourie - Updated]</author>
// <email>jasonbi</email>
// <date>2004-06-18</date>
// <summary>A helper class for the Gac.</summary>
//-----------------------------------------------------------------------
namespace Microsoft.Sdc.Tasks.GlobalAssemblyCache
{
    using System;
    using System.Reflection;
    using System.Runtime.InteropServices;
    
    /// <summary>
    /// Provides an interface to the global assembly cache via the
    /// IAssemblyCache COM interface.
    /// </summary>
    internal sealed class GacHelper
    {
        /// <summary>
        /// Private constructor as this class only contains statics.
        /// </summary>
        private GacHelper()
        {
        }
        
        /// <summary>
        /// Returns true if the global assembly cache contains the named
        /// assembly, or false otherwise.
        /// </summary>
        /// <param name="name">
        /// The name of the assembly to check for.
        /// </param>
        /// <returns>
        /// True if the assembly exists in the global assembly cache, or false
        /// otherwise.
        /// </returns>
        public static bool Contains(string name)
        {
            // Get the IAssemblyCache interface
            NativeMethods.IAssemblyCache assemblyCache = GetIAssemblyCache();

            // Query for the assembly info
            int result = assemblyCache.QueryAssemblyInfo(0, name, IntPtr.Zero);

            // Return true if the result is zero
            return result == 0;
        }

        /// <summary>
        /// Returns true if the global assembly cache contains the named
        /// assembly, or false otherwise.
        /// </summary>
        /// <param name="assemblyName">
        /// The AssemblyName to look for.
        /// </param>
        /// <returns>
        /// True if the assembly exists in the global assembly cache, or false
        /// otherwise.
        /// </returns>
        public static bool Contains(AssemblyName assemblyName)
        {
            // Call Contains with the full assmebly name
            return Contains(assemblyName.FullName);
        }

        /// <summary>
        /// Installs an assembly in the global assembly cache.
        /// </summary>
        /// <param name="path">
        /// The path to the assembly to install.
        /// </param>
        /// <param name="force">
        /// Normally if the assembly is already installed in the GAC and the
        /// file version numbers of the assembly being installed are the same
        /// or later, the files are replaced. If force is true the files of an
        /// existing assembly are overwritten regardless of their version
        /// number.
        /// </param>
        public static void Install(string path, bool force)
        {
            // Get the IAssemblyCache interface
            NativeMethods.IAssemblyCache assemblyCache = GetIAssemblyCache();

            // Set the flag depending on the value of force
            int flag = force ? 2 : 1;

            // Install the assembly in the cache
            int result = assemblyCache.InstallAssembly(flag, path, IntPtr.Zero);

            // If the result is not zero throw an exception
            if (result != 0)
            {
                throw new COMException("Failed to install assembly into the global assembly cache.", result);
            }
        }

        /// <summary>
        /// Installs an assembly in the global assembly cache.
        /// </summary>
        /// <param name="path">
        /// The path to the assembly to install.
        /// </param>
        public static void Install(string path)
        {
            // Call Install with the path and force as false
            Install(path, false);
        }

        /// <summary>
        /// Uninstalls an assembly from the global assembly cache.
        /// </summary>
        /// <param name="name">
        /// The name of the assembly to uninstall.
        /// </param>
        public static void Uninstall(string name)
        {
            // Get the IAssemblyCache interface
            NativeMethods.IAssemblyCache assemblyCache = GetIAssemblyCache();

            // Uninstall the assembly from the cache
            int disposition;
            int result = assemblyCache.UninstallAssembly(0, name, IntPtr.Zero, out disposition);

            // If the result is not zero or the disposition is not 1 then
            // throw an exception
            if (result != 0)
            {
                // If result is not 0 then something happened. Check the value of
                // disposition to see if we should throw an error. Determined the
                // values of the codes returned in disposition from
                switch (disposition)
                {
                    case 1:
                        // Assembly was removed from GAC.
                        break;
                    case 2:
                        throw new COMException("An application is using " + name + " so it could not be uninstalled.");
                    case 3:
                        // Assembly is not in the assembly. Don't throw an error, just
                        // proceed to install it.
                        break;
                    case 4:
                        // Not used.
                        break;
                    case 5:
                        throw new COMException(name + " was not uninstalled from the GAC because another reference exists to it.");
                    case 6:
                        // Problem where a reference doesn't exist to the pointer.
                        // We aren't using the pointer so this shouldn't be a problem.
                        break;
                    default:
                        throw new COMException("Failed to uninstall " + name + " from the GAC.");
                }
            }
        }

        /// <summary>
        /// Uninstalls an assembly from the global assembly cache.
        /// </summary>
        /// <param name="assemblyName">
        /// The AssemblyName to uninstall.
        /// </param>
        public static void Uninstall(AssemblyName assemblyName)
        {
            // Call Uninstall with the full assmebly name
            Uninstall(assemblyName.FullName);
        }

        /// <summary>
        /// Gets the IAssemblyCache interface.
        /// </summary>
        /// <returns>
        /// An IAssemblyCache interface.
        /// </returns>
        private static NativeMethods.IAssemblyCache GetIAssemblyCache()
        {
            // Get the IAssemblyCache interface
            NativeMethods.IAssemblyCache assemblyCache;
            int result = NativeMethods.CreateAssemblyCache(out assemblyCache, 0);

            // If the result is not zero throw an exception
            if (result != 0)
            {
                throw new COMException("Failed to get the IAssemblyCache interface.", result);
            }

            // Return the IAssemblyCache interface
            return assemblyCache;
        }              
    }
}